/**
 * @brief 协程调度器实现
 * @details 封装的是N-M的协程调度器
 * 内部有一个线程池,支持协程在线程池里面切换
 */

// 所谓协程调度，其实就是创建一批的协程对象，然后再创建一个调度协程，
// 通过调度协程把这些协程对象一个一个消化掉(协程可以在被调度时继续向调度器添加新的调度任务)

#ifndef __SCHEDULER_H__
#define __SCHEDULER_H__

#include "thread.h"
#include "fiber.h"
#include <string>
#include <vector>
#include <list>

class Scheduler
{
public:
    typedef std::shared_ptr<Scheduler> ptr;
    typedef Mutex MutexType;

    /**
     * @brief 创建调度器
     * @param[in] threads 线程数
     * @param[in] use_caller 是否将当前线程也作为调度线程
     * @param[in] name 调度器名称
     */
    Scheduler(size_t threads = 1, bool use_caller = true, const std::string &name = "Scheduler");

    virtual ~Scheduler();

    // 获取调度器名称
    const std::string &getName() const { return m_name; }

    // 获取当前线程调度器指针
    static Scheduler *GetThis();

    // 获取当前线程的主协程
    static Fiber *GetMainFiber();

    /**
     * @brief 添加调度任务
     * @tparam FiberOrCb 调度任务类型,可以是协程对象或函数指针
     * @param[] fc 协程对象或者指针
     * @param[] thread 指定运行该任务的线程号,-1表示任意线程
     */
    template <class FiberOrCb>
    void schedule(FiberOrCb fc, int thread_id = -1)
    {
        bool need_tickle = false;
        {
            MutexType::Lock lock(m_mutex);
            need_tickle = scheduleNoLock(fc, thread_id);
        }

        if (need_tickle)
        {
            tickle(); // 唤醒idle协程
        }
    }

    // 启动调度器
    void start();

    // 停止调度器,等所有的任务调度都执行完了再返回
    void stop();

protected:
    // 通知协程调度器有任务了
    virtual void tickle();

    // 协程调度函数
    void run();

    // 无任务调度时执行idle协程
    virtual void idle();

    // 返回是否可以停止
    virtual bool stopping();

    // 设置当前的协程调度器
    void SetThis();

    /**
     * @brief 返回是否有空闲线程
     * @details 当调度协程进入idle时空闲线程数加1,从idle协程返回时空闲线程减1
     */
    bool hasIdleThreads() { return m_idleThreadCount > 0; }

private:
    /**
     * @brief 添加调度任务,无锁
     * @tparam FiberOrCb 调度任务类型,可以是协程对象或者函数指针
     * @param[] fc 协程对象或函数指针
     * @param[] thread_id 指定运行该任务的线程号,-1表示任意线程
     */

    template <class FiberOrCb>
    bool scheduleNoLock(FiberOrCb fc, int thread_id)
    {
        // 需要触发调度器的‘tickle’操作 用于唤醒或激活等待中的任务
        bool need_tickle = m_tasks.empty();
        ScheduleTask task(fc, thread_id);
        if (task.fiber || task.callback)
        {
            m_tasks.push_back(task);
        }

        return need_tickle;
    }

private:
    /**
     * @brief 调度任务,协程/函数二选一,可以选择在哪个线程上调度
     */
    struct ScheduleTask
    {
        Fiber::ptr fiber;
        std::function<void()> callback;
        int thread_id;

        ScheduleTask(Fiber::ptr ptr, int id)
        {
            fiber = ptr;
            thread_id = id;
        }
        ScheduleTask(Fiber::ptr *ptr, int id)
        {
            fiber.swap(*ptr);
            thread_id = id;
        }

        ScheduleTask(const std::function<void()> &cb, int id)
        {
            callback = cb;
            thread_id = id;
        }
        ScheduleTask() { thread_id = -1; }

        void reset()
        {
            fiber = nullptr;
            callback = nullptr;
            thread_id = -1;
        }
    };

private:
    // 协程调度器名称
    std::string m_name;
    // 互斥锁
    MutexType m_mutex;
    // 线程池
    std::vector<Thread::ptr> m_threads;
    // 任务队列
    std::list<ScheduleTask> m_tasks;
    // 线程池的线程ID数组
    std::vector<int> m_threadIds;
    // 工作线程的数量,不包含use_caller的主线程
    size_t m_threadCount = 0;
    // 活跃线程数
    std::atomic<size_t> m_activeThreadCount = {0};
    // idle线程数
    std::atomic<size_t> m_idleThreadCount = {0};

    // 是否use_caller
    bool m_useCaller;
    // use_caller为true时,调度器所在的线程的调度协程
    Fiber::ptr m_rootFiber;
    // use_caller为true时,调度器所在线程的ID
    int m_rootThreadId = 0;

    // 是否正在停止
    bool m_stopping = false;
};

#endif
